package com.yungu.swift.autoconfig.redis;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands.Range;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Protocol;
import redis.clients.util.SafeEncoder;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author cuixiuyin
 * @date 2019/4/3
 */
public class RedisCacheService {


    private RedisTemplate<String, Object> redisTemplate;

    private String keyPrefix;

    public RedisCacheService(String keyPrefix, RedisTemplate redisTemplate) {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        this.redisTemplate = redisTemplate;
        this.keyPrefix = keyPrefix.endsWith("_") ? keyPrefix : keyPrefix + "_";
    }

    private String genKey(String key) {
        if (!StringUtils.isEmpty(key) && key.startsWith(keyPrefix)) {
            return key;
        }
        return keyPrefix + key;
    }


    /*-----------------普通缓存操作-----------------------*/

    /**
     * 给Redis缓存赋值（默认不会过期）
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(genKey(key), value);
    }

    /**
     * 给Redis缓存赋值 判断是否加前缀
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value, Boolean isFixKey) {
        if(isFixKey){
            set(key,value);
        }else{
            redisTemplate.opsForValue().set(key, value);
        }
    }

    /**
     * 给缓存赋值
     *
     * @param key
     * @param value
     * @param timeOut 存活时间(分钟)
     */
    public void set(String key, Object value, Long timeOut) {
        this.set(key, value, timeOut, TimeUnit.MINUTES);
    }

    /**
     * 给缓存赋值
     *
     * @param key
     * @param value
-     * @param timeUnit 单位
     */
    public void set(String key, Object value, Long timeOut, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(genKey(key), value, timeOut, timeUnit);
    }

    /**
     * 如果key不存在则设置值
     *
     * @param key
     * @param value
     * @param exptime
     * @return
     */
    public Boolean setIfAbsent(final String key, final Serializable value, final long exptime) {
        Boolean flag = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
                RedisSerializer keySerializer = redisTemplate.getKeySerializer();
                Object obj = connection.execute("set", keySerializer.serialize(genKey(key)),
                        valueSerializer.serialize(value),
                        SafeEncoder.encode("NX"),
                        SafeEncoder.encode("EX"),
                        Protocol.toByteArray(exptime));
                return obj != null;
            }
        });
        return flag;
    }

    /**
     * 判断是否包含给定key
     *
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(genKey(key));
    }

    /**
     * 模糊匹配得到所有的key
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(genKey(pattern));
    }

    /**
     * 删除key值的所有数据，包括列值、键值对、hash等
     *
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(genKey(key));
    }

    /**
     * 批量删除key
     *
     * @param keys
     */
    public void deleteKeys(Collection<String> keys) {
        List<String> keysList = keys.stream().map(key -> genKey(key)).collect(Collectors.toList());
        redisTemplate.delete(keysList);
    }

    /**
     * 得到缓存对象，默认加前缀
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        return get(key, true);
    }


    public Object get(String key, Boolean isFixKey) {
        if (isFixKey) {
            return redisTemplate.opsForValue().get(genKey(key));
        } else {
            return redisTemplate.opsForValue().get(key);
        }
    }

    /*-----------------集合操作-----------------------*/

    /**
     * 向List缓存对象尾部追加一个元素
     *
     * @param key
     * @param value
     */
    public void listRightPush(String key, Object... value) {
        redisTemplate.opsForList().rightPushAll(genKey(key), value);
    }


    public void listRightPushAll(String key, Collection<Object> collection) {
        redisTemplate.opsForList().rightPushAll(genKey(key), collection);
    }

    /**
     * 向List缓存对象头部追加一个元素
     *
     * @param key
     * @param value
     */
    public Long listLeftPush(String key, Object... value) {
        return redisTemplate.opsForList().leftPush(genKey(key), value);
    }

    /**
     * 向List缓存对象头部追加一个集合
     *
     * @param key
     * @param collection
     */
    public void listLeftPushAll(String key, Collection<Object> collection) {
        redisTemplate.opsForList().leftPushAll(genKey(key), collection);
    }

    /**
     * 集合尾部弹出一个元素
     * 副作用：同时会移除所弹出的元素
     *
     * @param key
     * @return
     */
    public Object listRightPop(String key) {
        Object[] objects = (Object[]) redisTemplate.opsForList().rightPop(genKey(key));
        return objects[0];
    }

    /**
     * 集合头部弹出一个元素
     * 副作用：同时会移除所弹出的元素
     *
     * @param key
     * @return
     */
    public Object listLeftPop(String key) {
        Object[] objects = (Object[]) redisTemplate.opsForList().leftPop(genKey(key));
        return objects[0];
    }

    /**
     * 获得对应缓存对象中的集合
     * 不会从列表中移除任何元素
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List listRange(String key, Long start, Long end) {
        return redisTemplate.opsForList().range(genKey(key), start, end);
    }

    /**
     * 在列表中index的位置设置value的值
     *
     * @param key
     * @param index
     * @param value
     */
    public void listSet(String key, Long index, Object value) {
        redisTemplate.opsForList().set(genKey(key), index, value);
    }

    /**
     * 获取列表中对应索引的值，下标从0开始
     *
     * @param key
     * @param index
     */
    public Object listIndex(String key, Long index) {
        return redisTemplate.opsForList().index(genKey(key), index);
    }

    /*----------------set 操作------------------------*/

    /**
     * 向一个Set集合中追加元素
     *
     * @param key
     * @param value
     */
    public void setAdd(String key, Object... value) {
        redisTemplate.opsForSet().add(genKey(key), value);
    }

    /**
     * 向一个Set集合中删除元素
     *
     * @param key
     * @param value
     */
    public void setRemove(String key, Object... value) {
        redisTemplate.opsForSet().remove(genKey(key), value);
    }

    /**
     * Set 集合随机返回一个元素并移除
     *
     * @param key
     * @return
     */
    public Object setPop(String key) {
        return redisTemplate.opsForSet().pop(genKey(key));
    }

    /**
     * 把一个Set集合中的value医道目标集合中
     *
     * @param key
     * @param value
     * @param destKey
     */
    public void setMove(String key, String value, String destKey) {
        redisTemplate.opsForSet().move(genKey(key), value, destKey);
    }

    /**
     * 集合随机返回一个元素不移除
     *
     * @param key
     * @return
     */
    public Object setRandomMember(String key) {
        return redisTemplate.opsForSet().randomMember(genKey(key));
    }

    /**
     * 集合随机返回一些元素
     *
     * @param key
     * @param count
     * @return
     */
    public List<Object> setListRandomMember(String key, Long count) {
        return redisTemplate.opsForSet().randomMembers(genKey(key), count);
    }

    /**
     * 判断这个value是否在这个集合中
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean setIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(genKey(key), value);
    }

    /**
     * 得到Set 集合中的所有元素
     *
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key) {
        return redisTemplate.opsForSet().members(genKey(key));
    }

    /**
     * otherKey 相对于 key 的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<Object> setDifference(String key, String otherKey) {
        return redisTemplate.opsForSet().difference(genKey(key), otherKey);
    }

    /**
     * 求两个集合的并集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<Object> setUnion(String key, String otherKey) {
        return redisTemplate.opsForSet().union(genKey(key), otherKey);
    }

    /**
     * 求两个集合的的交集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<Object> setIsect(String key, String otherKey) {
        return redisTemplate.opsForSet().intersect(genKey(key), otherKey);
    }

    /*------------------zset 操作----------------------*/

    /**
     * 向zset 中添加元素
     *
     * @param key
     * @param value
     * @param score
     */
    public void zsetAdd(String key, Object value, Double score) {
        redisTemplate.opsForZSet().add(genKey(key), value, score);
    }

    /**
     * Zset 中移除元素
     *
     * @param key
     * @param value
     */
    public void zsetRemove(String key, Object... value) {
        redisTemplate.opsForZSet().remove(genKey(key), value);
    }

    /**
     * 得到Zset中从start 到 end 的元素
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> zsetRange(String key, Long start, Long end) {
        return redisTemplate.opsForZSet().range(genKey(key), start, end);
    }

    /**
     * 按 Zset 的score得到元素
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<Object> zsetRangeByScore(String key, Double min, Double max) {
        return redisTemplate.opsForZSet().rangeByScore(genKey(key), min, max);
    }

    /**
     * 得到Zset集合中某个value的score
     *
     * @param key
     * @param value
     * @return
     */
    public Double zsetScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(genKey(key), value);
    }

    /**
     * 得到Zset集合的长度
     *
     * @param key
     * @return
     */
    public Long zsetSize(String key) {
        return redisTemplate.opsForZSet().size(genKey(key));
    }

    /**
     * score 在某个范围内的长度
     *
     * @param key
     * @param mix
     * @param max
     * @return
     */
    public Long zsetCount(String key, Double mix, Double max) {
        return redisTemplate.opsForZSet().count(genKey(key), mix, max);
    }

    /**
     * 按照值来排序的取值,这个排序只有在有相同分数的情况下才能使用，如果有不同的分数则返回值不确定
     *
     * @param key
     * @param range
     * @return
     */
    public Set<Object> zsetRangeByLex(String key, Range range) {
        return redisTemplate.opsForZSet().rangeByLex(genKey(key), range);
    }

    /*------------------ hash 操作----------------------*/

    /**
     * 向hash中存入值
     *
     * @param key
     * @param otherKey
     * @param otherValue
     */
    public void hashPut(String key, Object otherKey, Object otherValue) {
        redisTemplate.opsForHash().put(genKey(key), otherKey, otherValue);
    }

    /**
     * 向hash中存入map集合
     *
     * @param key
     * @param map
     */
    public void hashPutAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(genKey(key), map);
    }

    /**
     * 向hash 中删除值
     *
     * @param key
     * @param hashKeys
     */
    public void hashDelete(String key, Object... hashKeys) {
        redisTemplate.opsForHash().delete(genKey(key), hashKeys);
    }

    /**
     * 得到存放 key 的 Map 集合
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hashEntries(String key) {
        return redisTemplate.opsForHash().entries(genKey(key));
    }

    /**
     * 得到hash key 中 Map 的 key 的value
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hashGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(genKey(key), hashKey);
    }

    /**
     * 判断hash key 中 Map 的 key 是否存在
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Boolean hashHasKey(String key, String otherKey) {
        return redisTemplate.opsForHash().hasKey(genKey(key), otherKey);
    }

    /**
     * 得到Hash key 的 list 集合
     *
     * @param key
     * @return
     */
    public List<Object> hashValues(String key) {
        return redisTemplate.opsForHash().values(genKey(key));
    }


}
